﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.DirectInput;
using DirectInput = Microsoft.DirectX.DirectInput;

namespace JoyServer.Input
{
	/// <summary>
	/// ジョイスティックの内容を読み取るためのクラス
	/// </summary>
	public class JoystickReader
	{
		/// <summary>
		/// ジョイスティックの DirectInput デバイスのリスト
		/// 接続されている全てのジョイスティックを入れる
		/// </summary>
		List<Device> joysticks;

		/// <summary>
		/// コンストラクタ
		/// </summary>
		public JoystickReader()
		{
		}

		/// <summary>
		/// ジョイスティックの再走査
		/// </summary>
		public void resresh()
		{
			stop();
			joysticks = new List<Device>();

			// ジョイスティックのリストを取得
			DeviceList deviceList;
			deviceList = Manager.GetDevices(DeviceClass.GameControl, 
											EnumDevicesFlags.AttachedOnly);

			Debug.WriteLine("ジョイスティックの列挙 ***");
			foreach (DeviceInstance deviceInstance in deviceList)
			{
				Device device = new Device(deviceInstance.InstanceGuid);
				Debug.WriteLine("JoyStick: " +
								"ProductName: " + device.DeviceInformation.ProductName + ", " +
								"InstanceName: " + device.DeviceInformation.InstanceName);
				setAxisInputRange(device);
				joysticks.Add(device);
			}
		}

		/// <summary>
		/// ジョイスティックの読み取り準備開始
		/// </summary>
		/// <param name="hwnd">関連付けるウインドウのハンドル</param>
		public void start(IntPtr hwnd)
		{
			resresh();

			uint deviceNumber = 0;
			foreach (Device device in joysticks)
			{
				device.SetCooperativeLevel(
					null,
					CooperativeLevelFlags.NonExclusive |
					CooperativeLevelFlags.NoWindowsKey |
					CooperativeLevelFlags.Background);
				device.SetDataFormat(DeviceDataFormat.Joystick);
				acquire(deviceNumber);
				deviceNumber++;
			}
		}

		/// <summary>
		/// ジョイスティックの読み取り終了
		/// </summary>
		public void stop()
		{
			if (joysticks == null)
				return;

			foreach (Device device in joysticks)
			{
				if (device == null)
					continue;

				device.Unacquire();
				device.Dispose();
			}
			joysticks.Clear();
		}

		/// <summary>
		/// ジョイスティックの読み取り
		/// </summary>
		/// <param name="deviceNumber">デバイス番号</param>
		/// <returns>ジョイスティックのキー情報</returns>
		public Joystick read(uint deviceNumber)
		{
			//Debug.WriteLine("call input.read");
			if (deviceNumber >= joysticks.Count)
				return null;

			//Debug.WriteLine("call input.read: " + joysticks.Count);

			Joystick joystick = null;
			try
			{
				Device device = joysticks[(int)deviceNumber];
				JoystickState state;

				if (device == null)
				{
					return null;
				}
				device.Poll();
				state = joysticks[(int)deviceNumber].CurrentJoystickState;

				byte[] buttons = state.GetButtons();
				//int[] pov = state.GetPointOfView();

				joystick = new Joystick();
				joystick.buttonCount = buttons.Length;
				joystick.buttons = buttons;
				joystick.x = state.X;
				joystick.y = state.Y;
				joystick.z = state.Z;
			}
			catch (InputLostException)
			{
				acquire(deviceNumber);
				return null;
			}
			catch (NotAcquiredException)
			{
				acquire(deviceNumber);
			}
			catch (UnpluggedException)
			{
				//Device device = joysticks[(int)deviceNumber];
				//device.Unacquire();
				//device.Dispose();
				//device = null;
				return null;
			}
			catch (NullReferenceException)
			{
				return null;
			}

			//Debug.WriteLine("read ok");
			return joystick;
		}

		/// <summary>
		/// デバイスの接続
		/// </summary>
		/// <param name="deviceNumber"></param>
		public void acquire(uint deviceNumber)
		{
			//Debug.WriteLine("call input.acquire");
			Device device = joysticks[(int)deviceNumber];
			try
			{
				device.Acquire();
			}
			catch (UnpluggedException)
			{
				//device.Unacquire();
				//device.Dispose();
				//device = null;
				return;
			}
		}

		/// <summary>
		/// デバイスの名前一覧を取得する
		/// </summary>
		/// <returns></returns>
		public List<String> getDeviceNames()
		{
			List<String> names = new List<String>();

			foreach (Device device in joysticks)
			{
				names.Add(device.DeviceInformation.ProductName);
			}
			return names;
		}

		/// <summary>
		/// 十字キーの値の範囲を設定する
		/// -100 から 100 の間の値をとるように変更する。
		/// アナログスティックの場合、1 単位で数値が変わる。
		/// デジタルキーの場合、押すと最大値か最小値になる。
		/// </summary>
		private void setAxisInputRange(Device device)
		{
			foreach (DeviceObjectInstance deviceObjectInstance in device.Objects)
			{
				if ((deviceObjectInstance.ObjectId & (int)DeviceObjectTypeFlags.Axis) != 0)
				{
					device.Properties.SetRange(ParameterHow.ById,
											   deviceObjectInstance.ObjectId,
											   new InputRange(-100, 100));
				}
			}
			device.Properties.AxisModeAbsolute = true;
		}
	}
}
